/*	
	* Created by Varun Tyagi.
	*
	* This tutorial describes the first technique of collision detection.
	* We start by making a box around the entity and then on every iteration, we check to see if there is any collision.
*/

/* Include the header files. */
#include "SDL.h"
#include "SDL_image.h"
#include <string>

/* Screen Attributes. */
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int SCREEN_BPP = 32;

/* Frame Rate. */
const int FRAMES_PER_SECOND = 20;

/* Attributes of a square box. */
const int SQUARE_WIDTH = 20;
const int SQUARE_HEIGHT = 20;

/* The surface. */
SDL_Surface *pSquare = nullptr;
SDL_Surface *pScreen = nullptr;

/* Event structure. */
SDL_Event event;

/* The wall. */
SDL_Rect wall;

/* The Square class. */
class Square
{
private:

	/* The collision box of the square. */
	SDL_Rect box;

	/* The velocity of the square. */
	int m_iVelX, m_iVelY;

public:

	/* Initialize the box. */
	Square();

	/* Motion Handler. */
	void handle_input( void );

	/* Movement handler. */
	void handle_movement( void );

	/* Show the square. */
	void show_square( void );
};

/* The Timer class. */
class Timer
{
private:

	/* The time when the timer was started. */
	int m_iStartTime;

	/* The pause time. */
	int m_iPauseTime;

	/* The timer status. */
	bool m_bPaused;
	bool m_bStarted;

public:

	/* Initialize. */
	Timer();

	/* Clock actions. */
	void start( void );
	void stop( void );
	void pause( void );
	void unpause( void );

	/* Get the ticks. */
	int get_ticks( void );

	/* Status of the timer. */
	bool has_started( void );
	bool is_paused( void );
};

/* Image loader. */
SDL_Surface* load_image( std::string filename )
{
	/* Loaded image. */
	SDL_Surface *pLoadedImage;

	/* Optimized image. */
	SDL_Surface *pOptimizedImage;

	/* Load the image. */
	pLoadedImage = IMG_Load( filename.c_str() );

	/* Check the image. */
	if( nullptr != pLoadedImage )
	{
		/* Optimize the image. */
		pOptimizedImage = SDL_DisplayFormat( pLoadedImage );

		/* Check the image. */
		if( nullptr != pOptimizedImage )
		{
			/* Color key surface. */
			SDL_SetColorKey( pOptimizedImage, SDL_SRCCOLORKEY, SDL_MapRGB( pOptimizedImage->format, 0, 0xFF, 0xFF ) );
		}
	}

	/* Return the optimized images. */
	return pOptimizedImage;
}

/* Apply the surface. */
void apply_surface( int x, int y, SDL_Surface *pSource, SDL_Surface *pDestination, SDL_Rect *pRect = nullptr )
{
	/* Offset. */
	SDL_Rect offset;

	/* Get the offset. */
	offset.x = x;
	offset.y = y;

	/* Blit. */
	SDL_BlitSurface( pSource, pRect, pDestination, &offset );
}

/* Collision Checker. */
bool check_collision( SDL_Rect A, SDL_Rect B )
{
	/* The sides of the rectangle. */
	int iLeftA, iLeftB;
	int iRightA, iRightB;
	int iTopA, iTopB;
	int iBottomA, iBottomB;

	/* Calculate the sides of the rect. */
	
	/* A Rect. */
	iLeftA = A.x;
	iRightA = A.x + A.w;
	iTopA = A.y;
	iBottomA = A.y + A.h;

	/* B Rect. */
	iLeftB = B.x;
	iRightB = B.x + B.w;
	iTopB = B.y;
	iBottomB = B.y + B.h;

	/* If any of the sides from A are outside of B. */
	if( iBottomA <= iTopB )
	{
		return false;
	}

	if( iTopA >= iBottomB )
	{
		return false;
	}

	if( iRightA <= iLeftB )
	{
		return false;
	}

	if( iLeftA >= iRightB  )
	{
		return false;
	}

	/* If nothing from side A are outside B, there is a collision. */
	return true;
}

/* Initialize SDL. */
bool init_SDL( void )
{
	/* Initialize all the subsystems. */
	if( -1 == SDL_Init( SDL_INIT_EVERYTHING ) )
	{
		return false;
	}

	/* Set up the screen. */
	pScreen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE );

	/* Check the loaded screen. */
	if( nullptr == pScreen )
	{
		return false;
	}

	/* Set the window caption. */
	SDL_WM_SetCaption( "Collision Checker", nullptr );

	return true;
}

/* Load the files. */
bool load_files( void )
{
	/* Load the image. */
	pSquare = load_image( "square.bmp" );

	/* Check the loaded image. */
	if( nullptr == pSquare )
	{
		return false;
	}

	/* Everything went well, return true. */
	return true;
}

/* Clean up. */
void clean_SDL( void )
{
	/* Free the surface. */
	SDL_FreeSurface( pSquare );

	/* QUIT SDL. */
	SDL_Quit();
}


/********* SQAURE ***********/

Square::Square()
{
	/* Initialize the offset. */
	box.x = 0;
	box.y = 0;

	/* Set the square dimension. */
	box.w = SQUARE_WIDTH;
	box.y = SQUARE_HEIGHT;

	/* Initialize the velocity. */
	m_iVelX = m_iVelY = 0;
}

void Square::handle_input( void )
{
	/* Check for key presses. */
	if( SDL_KEYDOWN == event.type )
	{
		switch( event.key.keysym.sym )
		{
		case SDLK_UP:
		case SDLK_KP8:
			{
				m_iVelY -= SQUARE_HEIGHT / 2;
				break;
			}
		case SDLK_DOWN:
		case SDLK_KP2:
			{
				m_iVelY += SQUARE_HEIGHT / 2;
				break;
			}
		case SDLK_LEFT:
		case SDLK_KP4:
			{
				m_iVelX -= SQUARE_WIDTH / 2;
				break;
			}
		case SDLK_RIGHT:
		case SDLK_KP6:
			{
				m_iVelX += SQUARE_WIDTH / 2;
				break;
			}
		}
	}

	else if( SDLK_UP == event.type )
	{
		switch( event.key.keysym.sym )
		{
		case SDLK_UP:
		case SDLK_KP8:
			{
				m_iVelY += SQUARE_HEIGHT / 2;
				break;
			}
		case SDLK_DOWN:
		case SDLK_KP2:
			{
				m_iVelY -= SQUARE_HEIGHT / 2;
				break;
			}
		case SDLK_LEFT:
		case SDLK_KP4:
			{
				m_iVelX += SQUARE_WIDTH / 2;
				break;
			}
		case SDLK_RIGHT:
		case SDLK_KP6:
			{
				m_iVelX -= SQUARE_WIDTH / 2;
				break;
			}
		}
	}
}

void Square::handle_movement( void )
{
	/* Move the square left or right and also check for the collision. */
	box.x += m_iVelX;

	if( ( box.x < 0 ) || ( box.x + SQUARE_WIDTH > SCREEN_WIDTH ) || ( check_collision( box, wall ) ) )
	{
		/* Stop from moving. */
		box.x -= m_iVelX;
	}

	/* Move the box up and down. */
	box.y -= m_iVelY;

	if( (box.y < 0 ) || ( box.y + SQUARE_HEIGHT > SCREEN_HEIGHT ) || ( check_collision( box, wall ) ) )
	{
		/* Stop from moving. */
		box.y -= m_iVelY;
	}
}

void Square::show_square( void )
{
	/* Show the square. */
	apply_surface( box.x, box.y, pSquare, pScreen );
}

/********** TIMER ***********/

Timer::Timer()
{
	/* Initialize variables. */
	m_iStartTime = 0;
	m_iPauseTime = 0;
	m_bPaused = false;
	m_bStarted = false;
}

void Timer::start( void )
{
	/* Start the timer. */
	m_bStarted = true;

	/* Unpause if paused. */
	m_bPaused = false;

	/* Get the current time. */
	m_iStartTime = SDL_GetTicks();
}

void Timer::stop( void )
{
	/* Stop the timer. */
	m_iStartTime = false;

	/* Unpause the timer. */
	m_iPauseTime = false;
}

void Timer::pause( void )
{
	/* Check if the timer is running and not already paused. */
	if( ( true == m_bStarted ) && ( false == m_bPaused ) )
	{
		m_bPaused = true;

		/* Calculate pause Ticks. */
		m_iPauseTime = SDL_GetTicks() - m_iStartTime;
	}
}

void Timer::unpause( void )
{
	/* If the timer is paused. */
	if( true == m_bPaused )
	{
		/* Unpause the timer. */
		m_bPaused = false;

		/* Reset the timer. */
		m_iStartTime = SDL_GetTicks() - m_iPauseTime;

		/* Reset the pause timer. */
		m_iPauseTime = 0;
	}
}

int Timer::get_ticks( void )
{
	/* If the timer is running. */
	if( true == m_bStarted )
	{
		/* If the timer is paused. */
		if( true == m_bPaused )
		{
			/* Return the paused time. */
			return m_iPauseTime;
		}
		else
		{
			/* Return the current time. */
			return ( SDL_GetTicks() - m_iStartTime );
		}
	}

	/* Else return nothing. */
	return 0;
}

bool Timer::has_started( void )
{
	return m_bStarted;
}

bool Timer::is_paused( void )
{
	return m_bPaused;
}


/* Main */
int main( int argc, char** argv )
{
	/* QUIT Flag. */
	bool bQuit = false;

	/* The square. */
	Square MySq;

	/* FPS Regulator. */
	Timer fps;

	/* Initialize. */
	if( false == init_SDL() )
	{
		return 1;
	}

	/* Load the files. */
	if( false == load_files() )
	{
		return 1;
	}

	/* Set up the wall. */
	wall.x = 300;
	wall.y = 40;
	wall.w = 40;
	wall.h = 400;

	/* While the user hasn't quit. */
	while( false == bQuit )
	{
		/* Start the frame counter. */
		fps.start();

		/* While there is an event to handle. */
		while( SDL_PollEvent( &event ) )
		{
			/* Handle the events of the square. */
			MySq.handle_input();

			/* If the user has Xed out the window. */
			if( SDL_QUIT == event.type )
			{
				bQuit = true;
			}
		}

		/* Move the square. */
		MySq.handle_movement();

		/* Fill the screen white. */
		SDL_FillRect( pScreen, &pScreen->clip_rect, SDL_MapRGB(  pScreen->format, 0xFF, 0xFF, 0xFF ) );

		/* Show the wall. */
		SDL_FillRect( pScreen, &wall, SDL_MapRGB( pScreen->format, 0x77, 0x77, 0x77 ) );

		/* Show the square. */
		MySq.show_square();

		/* Update the screen. */
		if( SDL_Flip( pScreen ) == -1 )
		{
			return 1;
		}

		/* cap the frames. */
		if( fps.get_ticks() < 1000 / FRAMES_PER_SECOND )
		{
			SDL_Delay( ( 1000 / FRAMES_PER_SECOND ) - fps.get_ticks() );
		}
	}

	/* Clean up. */
	clean_SDL();

	return 1;
}




